/***************************************************************************
 *   Copyleft 2006-2007 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#include "./GregoryManager.h"
#include "../../resource.h"

/**************************************************************
* Constructors                                                *
***************************************************************/
GregoryManager::GregoryManager(Cursor3D* cursor, Engine3D* engine): Cursor3DManager(cursor) {
	mCreationState = INIT;
	mCreatedSurfaces = BEZIER_SURFACE_COUNT;
	for (cInt i = 0; i < BEZIER_SURFACE_COUNT; i++) {
		mBezierSurfaces[i] = cNull;
	}
	mBezierCurve1 = cNull;
	mBezierCurve2 = cNull;

	ProceedCreate(engine);
}

/*virtual*/ GregoryManager::~GregoryManager() {
	if (mCreationState != FINISHED)	{
		for (cInt i = 0; i < BEZIER_SURFACE_COUNT; i++) {
			if (mBezierSurfaces[i]) {
				delete mBezierSurfaces[i];
			}
		}
	}
	if (mManager) {
		delete mManager;
	}
	if (mBezierCurve1) {
		delete mBezierCurve1;
	}
	if (mBezierCurve2) {
		delete mBezierCurve2;
	}
}
    
/**************************************************************
* Operators                                                   *
***************************************************************/


/**************************************************************
* Other helpful functions                                     *
***************************************************************/
/*virtual*/ cVoid GregoryManager::OnCommand(HWND hwnd, WPARAM wParam, LPARAM lParam, Engine3D* engine) {
	cUShort cmd = LOWORD(wParam);
	if (mCreationState != FINISHED && (cmd == ID_ACCEL_ENTER 
		|| cmd == ID_SURFACE_CURVE1 || cmd == ID_SURFACE_CURVE2)) {
		ProceedCreate(engine);
		return;
	}
	mManager->OnCommand(hwnd, wParam, lParam, engine);
}

/*virtual*/ cVoid GregoryManager::OnMouseMove(cPoint point, Engine3D* engine) {
	mManager->OnMouseMove(point, engine);
}

/*virtual*/ cVoid GregoryManager::OnMouseWheel(cInt iDelta, Engine3D* engine) {
	mManager->OnMouseWheel(iDelta, engine);
}

/*virtual*/ cVoid GregoryManager::OnLeftMouseDown(cPoint point, Engine3D* engine) {
	mManager->OnLeftMouseDown(point, engine);
}

/*virtual*/ cVoid GregoryManager::OnLeftMouseUp(cPoint point, Engine3D* engine) {
	mManager->OnLeftMouseUp(point, engine);
}

/*virtual*/ cVoid GregoryManager::OnRightMouseDown(cPoint point, Engine3D* engine) {
	mManager->OnRightMouseDown(point, engine);
}

cVoid GregoryManager::ProceedCreate(Engine3D* engine) {
	Point4D wc;
	BezierSurface *bp;

	switch (mCreationState)	{
	case INIT:
		mBezierCurve1 = new BezierCurve();
		engine->mScene->AddObject(mBezierCurve1);

		mBezierCurve2 = new BezierCurve();
		engine->mScene->AddObject(mBezierCurve2);

		mManager = new BezierCurveManager(engine->mScene->GetCursor(), mBezierCurve1);
		mCreationState = CURVE_1;
		return;
	case CURVE_1:
		if (mBezierCurve1->GetNrOfPoints() < 4) {
			return;
		}
		wc = engine->mScene->GetCursor()->GetWorldPosition();
		delete mManager;
		mManager = cNull;

		CutCurve(mBezierCurve1);
		if (mBezierCurve1->GetNrOfPoints() > 0) {
			mBezierCurve2->AddPoint(mBezierCurve1->GetPoint(0));
		}

		mManager = new BezierCurveManager(engine->mScene->GetCursor(), mBezierCurve2);
		engine->mScene->GetCursor()->SetWorldPosition(wc, engine->GetWorldMatrix());
		mCreationState = CURVE_2;
		return;
	case CURVE_2:
		if (mBezierCurve2->GetNrOfPoints() < 4) {
			return;
		}
		wc = engine->mScene->GetCursor()->GetWorldPosition();
		delete mManager;
		mManager = cNull;

		CutCurve(mBezierCurve2);
		if (mCreatedSurfaces <= 1) {
			mBezierCurve1->ReplacePoint(mBezierSurfaces[0]->GetMeshPoint(0, 0), 3);
		}
		bp = new BezierSurface();
		bp->CreateSurface((AbstractCurve*) mBezierCurve1, (AbstractCurve*) mBezierCurve2);
		engine->mScene->AddObject(bp);

		mManager = new BezierSurfaceManager(bp, engine->mScene->GetCursor());
		mBezierSurfaces[BEZIER_SURFACE_COUNT - mCreatedSurfaces] = bp;

		engine->mScene->RemoveObject(mBezierCurve1);
		mBezierCurve1 = cNull;

		engine->mScene->RemoveObject(mBezierCurve2);
		mBezierCurve2 = cNull;

		engine->mScene->GetCursor()->SetWorldPosition(wc, engine->GetWorldMatrix());
		mCreationState = CREATED;
		return;
	case CREATED:
		if (--mCreatedSurfaces <= 0) {
			mCreationState = FINISHED;
			delete mManager;
			mManager = new TriGregoryManager(engine->mScene->GetCursor(), CreateGregory(engine));
			return;
		}
		delete mManager;
		mManager = cNull;
		mCreationState = INIT;
		ProceedCreate(engine);
		mBezierCurve1->AddPoint(mBezierSurfaces[BEZIER_SURFACE_COUNT - mCreatedSurfaces - 1]->GetMeshPoint(3, 0));
		return;
	case FINISHED:
		return;
	}
}

cVoid GregoryManager::CutCurve(BezierCurve *c) {
	cInt x = c->GetNrOfPoints();
	if (x > 4) {
		x -= 4;
		for (cInt i = 0; i < x; i++) {
			c->RemovePoint(4);
		}
	}
}

TriGregory* GregoryManager::CreateGregory(Engine3D* engine) {
	for (cInt i = 0; i < BEZIER_SURFACE_COUNT; i++) {
		mBezierSurfaces[i]->SwitchControlPointsVisibility();
		mBezierSurfaces[i]->SwitchMeshVisibility();
	}

	TriGregory *g = new TriGregory(mBezierSurfaces[0], mBezierSurfaces[1], mBezierSurfaces[2]);

	for (cInt i = 0; i < BEZIER_SURFACE_COUNT; i++) {
		engine->mScene->RemoveObject(mBezierSurfaces[i], false);
	}

	engine->mScene->AddObject(g);
	return g;
}

/*virtual*/ Object3D* GregoryManager::GetObject() {
	if (mCreationState == FINISHED) {
		return mManager->GetObject();
	} else {
		return cNull;
	}
}

cVoid GregoryManager::DisplayInfo(Renderer& renderer) {
	cChar pcBuff [MAX_BUF_SIZE];
	cInt iLineNr = 0;

	sprintf_s(pcBuff, "Triangular Gregory Hole Filling Info:");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		true, true, true, RGB(0, 255, 255));
	sprintf_s(pcBuff, "[F1] - Hit After Defining First Bezier Curve For Bezier Surface");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[F2] - Hit After Defining Second Bezier Curve For Bezier Surface");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[F3] - Hit After Defining Each Of Three Bezier Surfaces");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	iLineNr++;
	sprintf_s(pcBuff, "[Ctrl/Alt + J] - Increase/Decrease U");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[Ctrl/Alt + K] - Increase/Decrease V");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[Ctrl + B] - Show/Hide Control Points");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[Ctrl + V] - Show/Hide Mesh");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[Ctrl + C] - Show/Hide Surface");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "Space - Deselect Points");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
		sprintf_s(pcBuff, "LMB & Drag - Move Selected Points");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
}